package Result;

import java.util.ArrayList;
import java.util.List;

import Definitions.NodeClass;
import Sampling.SamplingAbstractClass;
import Utility.WorkerUtilityClass;

public class EvaluationClass {
	
	SamplingAbstractClass currentSampling;
	int confMat[][];
	
	ResultDataClass resultsData;
	

	
	public ResultDataClass getResultsData() {
		return resultsData;
	}

	public void setResultsData(ResultDataClass resultsData) {
		this.resultsData = resultsData;
	}

	public EvaluationClass(SamplingAbstractClass currentSampling)
	{
		this.currentSampling = currentSampling;
		resultsData = new ResultDataClass();
	}
	
	public void createConfMatAndPrepareTheResults()
	{
		createConfMat();
		resultsData.setAccuracy(findTotalAccuracy());
	}
	
	public ResultDataClass getMeasures()
	{
		return resultsData;
	}
	
	public int[][]  createConfMat() 
	{
		try
		{
			int classSize= currentSampling.getGlobal().classList.size();
			confMat = new int [classSize][classSize];

			ArrayList <NodeClass> nodelist = currentSampling.getTestNodes();

			for(int i= 0; i<classSize ; i++)
			{
				for(int j= 0; j<classSize ; j++)
				{
					confMat[i][j] = 0;
				}
			}		

			for(NodeClass node : nodelist)
			{
				int k=  currentSampling.getClassLabelEstimated(node);
				int m=  currentSampling.getActualClassLabelsForSelectedNode(node);

				//System.out.println("est:"+k+" act:"+m);			
				if(k<0)
				{
					System.out.println("ERROR:There are class labels still not estimated");
					System.exit(0);
				}		
				confMat [k][m]++;	
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}

		//printConfusionMatrix();
		return confMat;
	}

	public int[][]  CreateConfMat(ArrayList <NodeClass> nodelist)
	{
		return CreateConfMat(currentSampling, nodelist);
	}
	
	public static int[][]  CreateConfMat(SamplingAbstractClass sampling, List <NodeClass> nodelist) 
	{
		int classSize= sampling.getGlobal().classList.size();
		int confMat[][] = new int [classSize][classSize];
		
		for(int i= 0; i<classSize ; i++)
		{
			for(int j= 0; j<classSize ; j++)
			{
				confMat[i][j] = 0;
			}
		}		
		
		for(NodeClass node : nodelist)
		{
			int k=  sampling.getClassLabelEstimated(node);
			int m=  sampling.getActualClassLabelsForSelectedNode(node);
			
			//System.out.println("est:"+k+" act:"+m);			
			if(k<0)
			{
				System.out.println("ERROR:There are class labels still not estimated");
				System.exit(0);
			}		
			confMat [k][m]++;	
		}
		
		//printConfusionMatrix();
		return confMat;
	}
	
	public void printConfusionMatrix()
	{
		int classSize= currentSampling.getGlobal().classList.size();
		
		WorkerUtilityClass.printMatrix(confMat, classSize, classSize, "Confusion matrix");		
	}
	
	public static double[] findFMeasure(SamplingAbstractClass sampling, List<NodeClass> nodeList)
	{
		int[][] confMat = CreateConfMat(sampling, nodeList);
		int classSize = sampling.getGlobal().classList.size();
		
		double sumOfColumn[] =new double[classSize];
		double sumOfRows[] =new double[classSize];
		int truePositives[] = new int[classSize];
		double fMeasure[] = new double[classSize];
		
		for(int i=0; i<classSize; i++)
		{
			for(int j=0; j<classSize; j++)
			{
				sumOfRows[i] += confMat[i][j];
				sumOfColumn[j] +=  confMat[i][j];
			}
		}
		
		for(int i=0; i<classSize; i++)
		{
			if(sumOfRows[i] == 0)
			{
				sumOfRows[i] = 0;
			}
			else
			{
				sumOfRows[i] =  confMat[i][i] / sumOfRows[i];
			}
				
			
			if(sumOfColumn[i] == 0 )
			{
				sumOfColumn[i] = 0;
			}
			else 
			{
				sumOfColumn[i] =  confMat[i][i] / sumOfColumn[i];
			}
			
			if(sumOfColumn[i] == 0 || sumOfRows[i] == 0)
			{
				fMeasure[i] = 0;
			}
			else 
			{
				fMeasure[i] = (double)(2*(sumOfRows[i]*sumOfColumn[i]))/(double)(sumOfRows[i]+sumOfColumn[i]);
			}
		}
		
		return fMeasure;  
	}
	
	public static double[] findClasifierAccuracyForAllClasses(SamplingAbstractClass sampling, ArrayList<NodeClass> nodeList)
	{
		int[][] confMat = CreateConfMat(sampling, nodeList);
		int classSize = sampling.getGlobal().classList.size();
		double [] accuracy = new double[classSize];
		for(int i=0;  i<classSize ; i++)
		{
			int sum=0; 
			int valid =0;

			for (int j = 0; j < classSize; j++) 
			{
				sum += confMat[i][j];
				
				if (i == j)
				{
					valid += confMat[i][j];
				}
			}
			
			if(sum>0)
			{
				accuracy[i] = ((double)valid)/((double)sum); 
			}
		}
		return accuracy;

	}
		
	public double[] findClasifierAccuracyForAllClasses(int[][] confMat)
	{
		int classSize = currentSampling.getGlobal().classList.size();
		double [] accuracy = new double[classSize];
		for(int i=0;  i<classSize ; i++)
		{
			int sum=0; 
			int valid =0;

			for (int j = 0; j < classSize; j++) 
			{
				sum += confMat[i][j];
				
				if (i == j)
				{
					valid += confMat[i][j];
				}
			}
			
			if(sum>0)
			{
				accuracy[i] = ((double)valid)/((double)sum); 
			}
		}
		return accuracy;
	}
	
	public static double findTotalAccuracy(SamplingAbstractClass sampling, ArrayList<NodeClass> nodeList)
	{
		int[][] confMat = CreateConfMat(sampling, nodeList);

		int classSize= sampling.getGlobal().classList.size();
		int sum=0; 
		int valid =0;

		for(int i=0;  i<classSize ; i++)
		{
			for (int j = 0; j < sampling.getGlobal().classList.size(); j++) 
			{
				sum += confMat[i][j];
				
				if (i == j)
				{
					valid += confMat[i][j];
				}
			}
		}
		
		if(sum!=0)
			return ((double)valid)/((double)sum);
		else
			return 0;

		
	}
	
	public double findTotalAccuracy(int[][] confMat)
	{
		int classSize= currentSampling.getGlobal().classList.size();
		int sum=0; 
		int valid =0;

		for(int i=0;  i<classSize ; i++)
		{
			for (int j = 0; j < currentSampling.getGlobal().classList.size(); j++) 
			{
				sum += confMat[i][j];
				
				if (i == j)
				{
					valid += confMat[i][j];
				}
			}
		}
		
		if(sum!=0)
			return ((double)valid)/((double)sum);
		else
			return 0;
	}
	
	public double findTotalAccuracy()
	{
		int classSize= currentSampling.getGlobal().classList.size();
		int sum=0; 
		int valid =0;

		for(int i=0;  i<classSize ; i++)
		{
			for (int j = 0; j < currentSampling.getGlobal().classList.size(); j++) 
			{
				sum += confMat[i][j];
				
				if (i == j)
				{
					valid += confMat[i][j];
				}
			}
		}
		
		if(sum!=0)
			return ((double)valid)/((double)sum);
		else
			return 0;
	}
	
	/*TODO KADRIYEB find error bar*/
	//findFmeasure
	//findPrecision
	//FindRecall
	
	//create dummy object
	public static void Test (ArrayList<NodeClass> node)
	{
		
	}


}
